package com.zhangfuxing.datatools.core;

import com.zhangfuxing.datatools.logger.Log;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.*;
import java.util.*;
import java.util.stream.Stream;

import static java.nio.file.StandardOpenOption.APPEND;

/**
 * @author 张福兴
 * @version 1.0
 * @date 2024/10/2
 * @email zhangfuxing1010@163.com
 */
public class EntryPoint implements Runnable {
    public static final List<String> history = new ArrayList<>();
    CommandReader commandReader;
    final AppConfig appConfig;
    Connection connection;

    public EntryPoint() {
        this.appConfig = AppContext.getBean(AppConfig.class);
        this.commandReader = AppContext.getBean(CommandReader.class);
    }


    @Override
    public void run() {
        initAppConfig();
        while (true) {
            try {
                int i = execCommand(commandReader.readAll());
                if (i == 1) {
                    break;
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public int execCommand(String input) {
        try {
            Command command = Command.parse(input);
            if (command == null) {
                return 0;
            }
            if (command == Command.EXIT) {
                return 1;
            }
            String sql = command.getSQL();
            PreparedStatement ps = connection.prepareStatement(sql);

            int result;
            if (sql.toLowerCase().startsWith("select") || sql.toLowerCase().startsWith("show")) {
                ResultSet resultSet = ps.executeQuery();
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                String columns = "";
                List<String> values = new ArrayList<>();
                boolean initHeader = false;
                while (resultSet.next()) {
                    StringJoiner header = new StringJoiner(", ");
                    StringJoiner row = new StringJoiner(", ");
                    for (int i = 1; i <= columnCount; i++) {
                        if (!initHeader) {
                            String columnName = metaData.getColumnName(i);
                            header.add(columnName);
                        }
                        Object value = resultSet.getObject(i);
                        row.add(value.toString());
                    }
                    initHeader = true;
                    columns = String.valueOf(header);
                    values.add(row.toString());
                }

                resultSet.close();
                if (command == Command.SAVE) {
                    Log.info("请输入保存路径: ");
                    String savePath = commandReader.readLine();
                    history.add(savePath);
                    if (savePath == null) {
                        savePath = "./" + UUID.randomUUID() + ".txt";
                    }
                    if (!savePath.contains("/")) {
                        savePath = "./" + savePath;
                    }

                    Path path = Paths.get(savePath);
                    Path parent = path.getParent();
                    if (!Files.exists(path)) {
                        Files.createDirectories(parent);
                    }
                    Files.createFile(path);

                    List<String> h = new ArrayList<>();
                    h.add(columns);
                    Files.write(path, h, StandardCharsets.UTF_8, APPEND);
                    Files.write(path, values, StandardCharsets.UTF_8, APPEND);
                    return 0;
                }
                Log.info(columns);
                for (String value : values) {
                    Log.info(value);
                }

            } else {
                result = ps.executeUpdate();
                Log.info("受影响行数：%d", result);
            }

        } catch (Exception e) {
            Log.error("命令执行失败: %s", e, e.getMessage());
        }
        return 0;
    }

    public void initAppConfig() {
        Path confPath = Paths.get("./data-tools.conf");
        if (!Files.exists(confPath)) {
            Log.info("程序找不到配置文件，请在程序当前路径下创建data-tools.conf文件，填写如下内容：%s", "");
            Log.info("输入 /exit 退出程序。");
            return;
        }
        try {
            List<String> lines = Files.readAllLines(confPath, StandardCharsets.UTF_8);
            for (String line : lines) {
                if (line.startsWith("#") || line.startsWith("//")) {
                    continue;
                }
                String content = line.replaceAll("\\s+", "");
                if (content.startsWith("driverClassName")) {
                    appConfig.setDriverClassName(content.substring("driverClassName".length() + 1));
                }
                if (content.startsWith("url")) {
                    appConfig.setUrl(content.substring("url".length() + 1));
                }
                if (content.startsWith("username")) {
                    appConfig.setUsername(content.substring("username".length() + 1));
                }
                if (content.startsWith("password")) {
                    appConfig.setPassword(content.substring("password".length() + 1));
                }
                if (content.startsWith("libs")) {
                    appConfig.setLibs(content.substring("libs".length() + 1));
                }
            }

        } catch (IOException e) {
            Log.error("读取配置文件失败：%s%n", e, e.getMessage());
        }
        initConnection();
    }

    public void initConnection() {
        String libs = appConfig.getLibs();
        if (libs == null || libs.isEmpty()) {
            Log.error("请指定需要连接的数据库依赖库：libs", null);
            return;
        }
        try {
            Path path = Paths.get(libs);
            String[] urls;
            if (Files.isDirectory(path)) {
                try (Stream<Path> walk = Files.walk(path, 1)) {
                    urls = walk.map(Path::toString)
                            .filter(p -> p.endsWith(".jar"))
                            .toArray(String[]::new);
                }

            } else {
                if (!libs.endsWith(".jar")) {
                    Log.error("请指定有效的数据库依赖 xxx.jar", null);
                    return;
                }
                urls = new String[]{path.toString()};
            }

            URL[] urlArray = Arrays.stream(urls)
                    .map(lib -> {
                        try {
                            return new URL("jar:file:" + lib + "!/");
                        } catch (MalformedURLException e) {
                            throw new RuntimeException(e);
                        }
                    }).toArray(URL[]::new);
            URLClassLoader classLoader = new URLClassLoader(urlArray);
            ServiceLoader<Driver> services = ServiceLoader.load(Driver.class, classLoader);
            Iterator<Driver> iterator = services.iterator();
            Driver driver = null;
            while (iterator.hasNext()) {
                Driver item = iterator.next();
                if (item.acceptsURL(appConfig.getUrl())) {
                    driver = item;
                    break;
                }
            }
            if (driver == null) {
                String driverClassName = appConfig.getDriverClassName();
                if (driverClassName == null) {
                    Log.info("请输入driverClassName");
                    CommandReader.inputTips.run();
                    driverClassName = commandReader.readLine();
                    history.add(driverClassName);
                    appConfig.setDriverClassName(driverClassName);
                    if (driverClassName == null) {
                        Log.error("初始化错误", null);
                        return;
                    }
                }
                Class<?> driverClass = Class.forName(driverClassName, true, classLoader);
                Constructor<?> constructor = driverClass.getDeclaredConstructor();
                driver = (Driver) constructor.newInstance();
            }

            Properties properties = new Properties();
            properties.put("user", appConfig.getUsername());
            properties.put("password", appConfig.getPassword());
            if (appConfig.getDriverClassName() == null || appConfig.getDriverClassName().isEmpty()) {
                appConfig.setDriverClassName(driver.getClass().getName());
            }
            connection = driver.connect(appConfig.getUrl(), properties);
        } catch (Exception e) {
            Log.error("连接建立失败: 输入 /exit 退出", null);
        }
    }
}
